import openai
import os
import chardet
import time
import docx  # 导入docx处理库
from typing import List, Dict, Tuple

class SmartExamBot:
    def __init__(self, api_key: str, base_url: str, max_questions: int = 5):
        """
        初始化考试机器人
        :param api_key: OpenAI API密钥
        :param base_url: API基础URL
        :param max_questions: 最大题目数量
        """
        self.api_key = api_key
        self.base_url = base_url
        self.client = openai.OpenAI(api_key=api_key, base_url=base_url)
        self.max_follow_ups = 3  # 每道题最大追问次数
        self.question_count = 0  # 题目计数
        self.total_score = 0  # 总分
        self.max_possible_score = 0  # 可能获得的最高分
        self.max_questions = max_questions  # 最大题目数量
        self.current_follow_ups = 0  # 当前题目的追问次数

    def detect_encoding(self, file_path: str) -> str:
        """检测文件编码，仅用于文本文件"""
        if file_path.lower().endswith('第3章 数据获取.docx'):
            return 'docx'  # 特殊标记，表示docx文件
        try:
            with open(file_path, 'rb') as file:
                result = chardet.detect(file.read(10240))
                return result['encoding'] or 'utf-8'
        except Exception as e:
            raise Exception(f"编码检测失败: {str(e)}")

    def read_file_content(self, file_path: str) -> str:
        """读取文件内容，支持txt和docx格式"""
        # 验证文件存在性
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")

        if not os.path.isfile(file_path):
            raise IsADirectoryError(f"{file_path}不是一个文件")

        try:
            encoding = self.detect_encoding(file_path)

            # 处理docx文件
            if encoding == 'docx':
                doc = docx.Document(file_path)
                full_text = []

                # 提取段落文本
                for para in doc.paragraphs:
                    full_text.append(para.text)

                # 提取表格内容
                for table in doc.tables:
                    for row in table.rows:
                        row_text = [cell.text for cell in row.cells]
                        full_text.append('\t'.join(row_text))

                return '\n'.join(full_text)

            # 处理文本文件
            encodings_to_try = [encoding, 'utf-8', 'gbk', 'latin-1']
            for enc in encodings_to_try:
                try:
                    with open(file_path, 'r', encoding=enc, errors='replace') as file:
                        return file.read()
                except UnicodeDecodeError:
                    continue
            raise Exception("无法识别文件编码")
        except Exception as e:
            raise Exception(f"文件读取失败: {str(e)}")

    def get_ai_response(self, messages: List[Dict], max_retries: int = 3) -> str:
        """获取AI响应，带重试机制"""
        retry_count = 0
        while retry_count < max_retries:
            try:
                response = self.client.chat.completions.create(
                    model="gpt-4-turbo",
                    messages=messages,
                    timeout=30  # 设置超时时间开始
                )
                return response.choices[0].message.content
            except Exception as e:
                retry_count += 1
                if retry_count >= max_retries:
                    raise Exception(f"API请求失败: {str(e)}")
                print(f"API调用失败，正在重试({retry_count}/{max_retries})...")
                time.sleep(2)  # 重试间隔

    def start_exam(self, knowledge_content: str):
        """开始考试流程"""
        print("\n" + "=" * 50)
        print("智能考试机器人".center(40))
        print("=" * 50)
        print("\n我将根据提供的知识内容对你进行考察。")
        print(f"考试共{self.max_questions}题，每道题会有最多{self.max_follow_ups}次追问。")
        print("输入'开始'即可开始考试")
        print("输入'退出'可随时结束考试。\n")

        # 初始化对话历史
        messages = [
            {"role": "system", "content": f"""
            你是一个专业严谨的智能考试机器人，基于以下知识内容出题和评分:
            {knowledge_content}

            评分标准:
            1. 答案准确性(0-4分)
            2. 回答完整性(0-3分)
            3. 逻辑清晰度(0-3分)

            每次评分后请严格按以下格式返回，不要添加任何前缀、后缀或额外说明:
            分数(0-10)|具体反馈|是否追问(是/否)
            """},
            {"role": "assistant", "content": "我已准备好开始考试。请输入'开始'进行第一题。"}
        ]

        while True:
            # 检查是否达到最大题目数量
            if self.question_count >= self.max_questions:
                print("\n已完成所有题目！")
                self._show_final_score()
                break

            user_input = input("\n你: ").strip()

            if user_input.lower() in ['退出', 'q', 'quit']:
                self._show_final_score()
                break

            if user_input.lower() in ['开始', '继续', 'next']:
                self._handle_new_question(messages)
            else:
                # 验证输入有效性
                if not user_input:
                    print("请输入有效的回答内容")
                    continue
                self._handle_user_answer(user_input, messages)

    def _handle_new_question(self, messages: List[Dict]):
        """处理新题目"""
        self.question_count += 1
        self.current_follow_ups = 0  # 重置当前题目的追问次数

        print(f"\n===== 第{self.question_count}/{self.max_questions}题 =====")
        # 获取新题目
        messages.append({"role": "user", "content": "请出一道有深度的主观题，考察对知识的理解和应用能力"})
        question = self.get_ai_response(messages)
        messages.append({"role": "assistant", "content": question})

        print(f"考试机器人: {question}")
        self.max_possible_score += 10  # 每道题满分10分

    def _handle_user_answer(self, user_answer: str, messages: List[Dict]):
        """处理用户回答"""
        messages.append({"role": "user", "content": user_answer})

        # 打印用户回答，便于调试
        print(f"\n用户回答: {user_answer}")

        # 获取评估
        evaluation = self.get_ai_response(messages + [
            {"role": "assistant", "content": "请严格按照要求的格式进行评分和反馈，不要添加任何额外内容"}
        ])

        # 打印原始AI返回内容，便于调试
        print(f"\nAI评分返回: {evaluation}")

        try:
            # 尝试严格按"分数(0-10)|具体反馈|是否追问(是/否)"格式解析
            # 先移除可能的前缀和后缀文本
            clean_evaluation = self._clean_evaluation_text(evaluation)

            # 分割评分结果并清理空白字符
            parts = [p.strip() for p in clean_evaluation.split("|")]
            if len(parts) != 3:
                raise ValueError(f"评分格式不正确，预期3个部分，实际得到{len(parts)}个部分: {clean_evaluation}")

            score_part, feedback, need_follow_up_str = parts

            # 从"分数(3)"或类似格式中提取数字
            score = self._extract_score(score_part)

            # 验证分数范围
            if not (0 <= score <= 10):
                raise ValueError(f"评分超出范围(0-10)，得到{score}")

            need_follow_up = need_follow_up_str.lower() == '是'

            # 根据是否是追问调整分数权重
            if self.current_follow_ups > 0:
                # 追问的分数权重递减
                weight = 1.0 - (self.current_follow_ups * 0.2)
                adjusted_score = score * weight
                self.total_score += adjusted_score
                print(
                    f"\n[追问得分({self.current_follow_ups}/{self.max_follow_ups})] {adjusted_score:.1f}/{score:.1f} (权重调整后)")
            else:
                self.total_score += score
                print(f"\n评分: {score:.1f}/10")

            print(f"反馈: {feedback}")
            print(f"累计得分: {self.total_score:.1f}/{self.max_possible_score}")

            if need_follow_up and self.current_follow_ups < self.max_follow_ups:
                self.current_follow_ups += 1
                self._ask_follow_up(messages)
            else:
                print(f"\n本题结束，当前进度: {self.question_count}/{self.max_questions}题")
                print("输入'继续'进入下一题，或输入'退出'结束考试")

        except Exception as e:
            print(f"\n评分解析失败: {str(e)}")
            print(f"原始评分数据: {evaluation}")
            # 提供备选方案，手动继续
            print("请输入'继续'进入下一题")

    def _clean_evaluation_text(self, text: str) -> str:
        """清理评分文本，移除可能的前缀和后缀"""
        # 移除常见的前缀文本
        prefixes = ["评分:", "评估:", "得分:", "分数:"]
        for prefix in prefixes:
            if text.startswith(prefix):
                text = text[len(prefix):].strip()

        # 移除常见的后缀文本
        suffixes = ["\n", ".", "!", "。"]
        while any(text.endswith(suffix) for suffix in suffixes):
            for suffix in suffixes:
                if text.endswith(suffix):
                    text = text[:-len(suffix)].strip()
                    break

        return text

    def _extract_score(self, score_part: str) -> float:
        """从各种可能的分数格式中提取数值"""
        try:
            # 尝试直接转换
            return float(score_part)
        except ValueError:
            # 尝试从"分数(X)"格式提取
            import re
            match = re.search(r'(\d+(?:\.\d+)?)', score_part)
            if match:
                return float(match.group(1))
            else:
                # 无法提取有效分数，返回默认值
                print(f"警告: 无法从 '{score_part}' 中提取有效分数，使用默认值0分")
                return 0.0

    def _ask_follow_up(self, messages: List[Dict]):
        """提出追问问题"""
        follow_up_question = self.get_ai_response(messages + [
            {"role": "assistant", "content": "请基于用户的回答和之前的问题，提出一个有针对性的追问问题，深入考察理解程度"}
        ])
        messages.append({"role": "assistant",
                         "content": f"[追问 {self.current_follow_ups}/{self.max_follow_ups}] {follow_up_question}"})
        print(f"\n考试机器人 [追问 {self.current_follow_ups}/{self.max_follow_ups}]: {follow_up_question}")

    def _show_final_score(self):
        """显示最终得分"""
        print("\n" + "=" * 50)
        if self.question_count > 0:
            percentage = (self.total_score / self.max_possible_score) * 100 if self.max_possible_score > 0 else 0
            print(f"\n考试结束！共完成 {self.question_count} 道题")
            print(f"最终得分: {self.total_score:.1f}/{self.max_possible_score} ({percentage:.1f}%)")

            if percentage >= 80:
                print("成绩优秀！你对知识点的掌握非常扎实。")
            elif percentage >= 60:
                print("成绩良好！对主要内容有较好理解，但还有提升空间。")
            else:
                print("需要加强学习！建议回顾相关知识点。")
        else:
            print("本次未完成任何题目。")
        print("=" * 50 + "\n")


if __name__ == "__main__":
    # 配置信息
    CONFIG = {
        "api_key": "sk-ThA0WI5NVgQ1xYUf8BriyRJfIqBWRvbV5rrzmAWBvdjxsnWP",
        "base_url": "https://yibuapi.com/v1",
        "file_path": r"D:\桌面\竞赛\挑战杯+人工智能\第3章 数据获取.docx",  # 支持docx文件
        "max_questions": 5  # 最大题目数量
    }

    try:
        # 创建考试机器人实例
        exam_bot = SmartExamBot(
            api_key=CONFIG["api_key"],
            base_url=CONFIG["base_url"],
            max_questions=CONFIG["max_questions"]
        )

        # 读取知识内容
        knowledge_content = exam_bot.read_file_content(CONFIG["file_path"])
        print(f"成功加载知识内容，长度: {len(knowledge_content)} 字符")
        print("知识内容预览:", knowledge_content[:200] + "...")  # 显示内容预览

        # 开始考试
        exam_bot.start_exam(knowledge_content)

    except Exception as e:
        print(f"程序运行出错: {str(e)}")
    finally:
        input("按回车键退出...")